﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Roslyn.Compilers.CSharp;
using Roslyn.Compilers;
using Roslyn.Compiler.Extension.Values;
using Roslyn.Compilers.Common;
using System.Collections;
using Roslyn.Services;
namespace Roslyn.Compiler.Extension.Attributes.Converter
{
    /// <summary>
    /// TODO: Wie soll denn die Initialisierung statt finden
    /// </summary>
    public class AnyAttributeConverter : AttributeConverter
    {
        #region Attribute Converter Methods
        /// <summary>
        /// Converts attribute and field to a new field. a.g. [Any(list)] string test => List<string> test
        /// TODO: Convert initializer? What should happen to the initializer
        /// </summary>
        /// <param name="fieldSyntax">the declaration syntax</param>
        /// <returns>the transformed field</returns>
        public override FieldDeclarationSyntax ConvertField(FieldDeclarationSyntax fieldSyntax)
        {
            
            return ConvertDeclaration(fieldSyntax);
        }

        public override PropertyDeclarationSyntax ConvertProperty(PropertyDeclarationSyntax propertySyntax)
        {
            return ConvertPropertyDeclaration(propertySyntax);
        }

        public override MethodDeclarationSyntax ConvertMethod(MethodDeclarationSyntax methodSyntax)
        {
            return ConvertMethodDeclaration(methodSyntax);
        }

        #endregion

     
        

        #region Private Methods
        /// <summary>
        /// Converts attribute and field to a new field. a.g. [Any(list)] string test => List<string> test
        /// </summary>
        /// <param name="fieldSyntax">the declaration syntax</param>
        /// <returns>the transformed field</returns>
        private FieldDeclarationSyntax ConvertDeclaration(FieldDeclarationSyntax fieldSyntax)
        {
          
            TypeSyntax type = Syntax.ParseTypeName(fieldSyntax.Declaration.Type.ToString());
            GenericNameSyntax annotatedType = (GenericNameSyntax)(AttributeSyntax.ArgumentList.Arguments.First().Expression as TypeOfExpressionSyntax).Type;
            
            //Convert the decleration site
            GenericNameSyntax newGenericType = ConvertAnyType(type, annotatedType);
            VariableDeclarationSyntax newDeclaration = fieldSyntax.Declaration.WithType(newGenericType);
            FieldDeclarationSyntax newField = fieldSyntax.WithDeclaration(newDeclaration);
           
            //Build up the initializer site
            EqualsValueClauseSyntax oldInitializer = newField.Declaration.Variables[0].Initializer;
            ExpressionSyntax initializerExpression = Syntax.ParseExpression(string.Format(" new {0}()", newGenericType.ToString()));
            EqualsValueClauseSyntax newInitializer = Syntax.EqualsValueClause(initializerExpression);
            VariableDeclaratorSyntax decleratorSyntax = newField.Declaration.Variables[0].WithInitializer(newInitializer);
            SeparatedSyntaxList<VariableDeclaratorSyntax> decleratorSyntaxes = new SeparatedSyntaxList<VariableDeclaratorSyntax>();
            decleratorSyntaxes = decleratorSyntaxes.Add(decleratorSyntax);
            VariableDeclarationSyntax variableDecleration = newField.Declaration.WithVariables(decleratorSyntaxes);
            newField = newField.WithDeclaration(variableDecleration);
            

            return fieldSyntax.WithDeclaration(newField.Declaration);
           
        }

        private PropertyDeclarationSyntax ConvertPropertyDeclaration(PropertyDeclarationSyntax propertySyntax)
        {
            TypeSyntax type = propertySyntax.Type;
            GenericNameSyntax annotatedType = (GenericNameSyntax)(AttributeSyntax.ArgumentList.Arguments.First().Expression as TypeOfExpressionSyntax).Type;

            //Build up the return type
            GenericNameSyntax newGenericType = ConvertAnyType(type, annotatedType);

            return propertySyntax.WithType(newGenericType);

        }

        private MethodDeclarationSyntax ConvertMethodDeclaration(MethodDeclarationSyntax methodSyntax)
        {
            TypeSyntax type = Syntax.ParseTypeName(methodSyntax.ReturnType.ToString());
            GenericNameSyntax annotatedType = (GenericNameSyntax)(AttributeSyntax.ArgumentList.Arguments.First().Expression as TypeOfExpressionSyntax).Type;
            
            //Build up the return type
            GenericNameSyntax newGenericType = ConvertAnyType(type, annotatedType);
         
            return methodSyntax.WithReturnType(newGenericType);
        }

        /// <summary>
        /// Converts the "type" into the generic type annotated  
        /// </summary>
        /// <param name="type"></param>
        /// <param name="annotatedType"></param>
        /// <returns></returns>
        private static GenericNameSyntax ConvertAnyType(TypeSyntax type, GenericNameSyntax annotatedType)
        {
            SeparatedSyntaxList<TypeSyntax> typeSyntaxes = new SeparatedSyntaxList<TypeSyntax>();
            typeSyntaxes = typeSyntaxes.Add(type);
            TypeArgumentListSyntax newTypeArguments = annotatedType.TypeArgumentList.WithArguments(typeSyntaxes);
            GenericNameSyntax newGenericType = Syntax.GenericName(annotatedType.Identifier, newTypeArguments);
            newGenericType = newGenericType.WithTrailingTrivia(Syntax.Space);
            return newGenericType;
        }

        #endregion

        #region Validation 
        /// <summary>
        /// TODO: better would be an approach where all the items inheriting from ICollection would be taken. Unfortunately 
        /// the interface list of type info is empty. Maybe inspect ICollection and get all subtypes?
        /// </summary>
        /// <param name="declarationSyntax"></param>
        /// <returns></returns>
        public override string Validate(FieldDeclarationSyntax declarationSyntax)
        {
            TypeSyntax typeSyntax = (AttributeSyntax.ArgumentList.Arguments.First().Expression as TypeOfExpressionSyntax).Type;
            CommonTypeInfo typeInfo = SemanticModel.GetTypeInfo(typeSyntax);
            if (!Constants.AllowedAnyParameter.parameterList.Contains(typeInfo.Type.ToDisplayString()))
                return string.Format("The Type {0} is not allowed as an Any Parameter", typeInfo.Type.ToDisplayString());
            return string.Empty;
        }

        #endregion



    
public  Queue KeyValuePair { get; set; }}
}
